/***************************************************************************
 *   Copyright (C) 2007 by Rui Maciel   *
 *   rui.maciel@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "json.h"
#include "log_lib.h"

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <memory.h>
#include <sys/types.h>


enum LEX_VALUE
{ LEX_MORE = 0,
    LEX_INVALID_CHARACTER,
    LEX_TRUE,
    LEX_FALSE,
    LEX_NULL,
    LEX_BEGIN_OBJECT,
    LEX_END_OBJECT,
    LEX_BEGIN_ARRAY,
    LEX_END_ARRAY,
    LEX_NAME_SEPARATOR,
    LEX_VALUE_SEPARATOR,
    LEX_STRING,
    LEX_NUMBER,
    LEX_ERROR,
    LEX_MEMORY
};


/* rc_string part */

#define RSTRING_INCSTEP 5
#define RSTRING_DEFAULT 8


enum rui_string_error_codes
{ RS_MEMORY, RS_OK = 1, RS_UNKNOWN };

typedef enum rui_string_error_codes rstring_code;


rcstring *
rcs_create (size_t length)
{
    rcstring *rcs;
    rcs = malloc (sizeof (rcstring));   /* allocates memory for a struct rcstring */
    if (rcs == NULL)
        return NULL;

    rcs->max = length;
    rcs->length = 0;

    rcs->text = malloc ((rcs->max + 1) * sizeof (char));
    if (rcs->text == NULL)
    {
        free (rcs);
        return NULL;
    }
    rcs->text[0] = '\0';

    return rcs;
}


void
rcs_free (rcstring ** rcs)
{
    /*assert (rcs != NULL);*/
    
    if (rcs != NULL && *rcs != NULL)
    {
        if ((*rcs)->text != NULL)
        {
            free ((*rcs)->text);
            (*rcs)->text = NULL;
        }
        free (*rcs);
        *rcs = NULL;
    }

}


rstring_code
rcs_resize (rcstring * rcs, size_t length)
{
    char *temp;
    /*assert (rcs != NULL);*/
    if(rcs == NULL) return RS_MEMORY;

    temp = realloc (rcs->text, sizeof (char) * (length + 1));   /* length plus '\0' */
    if (temp == NULL)
    {
        free (rcs);
        return RS_MEMORY;
    }
    rcs->text = temp;
    rcs->max = length;
    rcs->text[rcs->max] = '\0';
    return RS_OK;
}


rstring_code
rcs_catcs (rcstring * pre, const char *pos, const size_t length)
{
    /*assert (pre != NULL);
    assert (pos != NULL);*/
    if(pre == NULL || pos == NULL)return RS_MEMORY;

    if (pre->max < pre->length + length)
    {
        if (rcs_resize (pre, pre->length + length + RSTRING_INCSTEP) != RS_OK)
            return RS_MEMORY;
    }
    strncpy (pre->text + pre->length, pos, length);
    pre->text[pre->length + length] = '\0';
    pre->length += length;
    return RS_OK;
}


rstring_code
rcs_catc (rcstring * pre, const char c)
{
    /*assert (pre != NULL);*/
    if(pre == NULL)return RS_MEMORY;

    if (pre->max <= pre->length)
    {
        if (rcs_resize (pre, pre->max + RSTRING_INCSTEP) != RS_OK)
            return RS_MEMORY;
    }
    pre->text[pre->length] = c;
    pre->length++;
    pre->text[pre->length] = '\0';
    return RS_OK;
}


char *
rcs_unwrap (rcstring * rcs)
{
    char *out;
    /*assert (rcs != NULL);*/
    if(rcs == NULL) return NULL;

    if (rcs->text == NULL)
        out = NULL;
    else
    {
        out = realloc (rcs->text, sizeof (char) * (strlen (rcs->text) + 1));
    }

    free (rcs);
    /*rcs_free(&rcs);*/
    return out;
}



size_t
rcs_length (rcstring * rcs)
{
    /*TODO account for UTF8 
    assert (rcs != NULL);*/
    if(rcs == NULL)return 0;
    return rcs->length;
}


/* end of rc_string part */


enum json_error
json_stream_parse (FILE * file, json_t ** document)
{
    char buffer[1024];  /* hard-coded value */
    unsigned int error = JSON_INCOMPLETE_DOCUMENT;

    struct json_parsing_info state;

    /*assert (file != NULL);   must be an open stream 
    assert (document != NULL);   must be a valid pointer reference 
    assert (*document == NULL);  only accepts a null json_t pointer, to avoid memory leaks */

    if(file == NULL || document == NULL || *document == NULL) return error;

    json_jpi_init (&state); /* initializes the json_parsing_info object */

    while ((error == JSON_WAITING_FOR_EOF) || (error == JSON_INCOMPLETE_DOCUMENT))
    {
        if (fgets (buffer, 1024, file) != NULL)
        {
            switch (error = json_parse_fragment (&state, buffer))
            {
            case JSON_OK:
            case JSON_WAITING_FOR_EOF:
            case JSON_INCOMPLETE_DOCUMENT:
                break;

            default:
                json_free_value (&state.cursor);
                return error;
                break;
            }
        }
        else
        {
            if (error == JSON_WAITING_FOR_EOF)
                error = JSON_OK;
            else
            {
                /*TODO refine this error code */
                error = JSON_UNKNOWN_PROBLEM;
            }
        }
    }

    if (error == JSON_OK)
    {
        *document = state.cursor;
    }

    return error;
}


json_t *
json_new_value (const enum json_value_type type)
{
    json_t *new_object;
    /* allocate memory to the new object */
    new_object = malloc (sizeof (json_t));
    if (new_object == NULL)
        return NULL;

    /* initialize members */
    new_object->text = NULL;
    new_object->parent = NULL;
    new_object->child = NULL;
    new_object->child_end = NULL;
    new_object->previous = NULL;
    new_object->next = NULL;
    new_object->type = type;
    return new_object;
}


json_t *
json_new_string (const char *text)
{
    json_t *new_object;
    size_t length;

    /*assert (text != NULL);*/
    if(text == NULL) return NULL;

    /* allocate memory for the new object */
    new_object = malloc (sizeof (json_t));
    if (new_object == NULL)
        return NULL;

    /* initialize members */
    length = strlen (text) + 1;
    new_object->text = malloc (length * sizeof (char));
    if (new_object->text == NULL)
    {
        free (new_object);
        return NULL;
    }
    strncpy (new_object->text, text, length);
    new_object->parent = NULL;
    new_object->child = NULL;
    new_object->child_end = NULL;
    new_object->previous = NULL;
    new_object->next = NULL;
    new_object->type = JSON_STRING;
    return new_object;
}


json_t *
json_new_number (const char *text)
{
    json_t *new_object;
    size_t length;

    /*assert (text != NULL);*/
    if(text == NULL) return NULL;

    /* allocate memory for the new object */
    new_object = malloc (sizeof (json_t));
    if (new_object == NULL)
        return NULL;

    /* initialize members */
    length = strlen (text) + 1;
    new_object->text = malloc (length * sizeof (char));
    if (new_object->text == NULL)
    {
        free (new_object);
        return NULL;
    }
    strncpy (new_object->text, text, length);
    new_object->parent = NULL;
    new_object->child = NULL;
    new_object->child_end = NULL;
    new_object->previous = NULL;
    new_object->next = NULL;
    new_object->type = JSON_NUMBER;
    return new_object;
}


json_t *
json_new_object (void)
{
    return json_new_value (JSON_OBJECT);
}


json_t *
json_new_array (void)
{
    return json_new_value (JSON_ARRAY);
}


json_t *
json_new_null (void)
{
    return json_new_value (JSON_NULL);
}


json_t *
json_new_true (void)
{
    return json_new_value (JSON_TRUE);
}


json_t *
json_new_false (void)
{
    return json_new_value (JSON_FALSE);
}


static void
intern_json_free_value (json_t ** value)
{
    /*assert (value != NULL);
    assert ((*value) != NULL);
    assert ((*value)->child == NULL);*/

    if(value == NULL || (*value) == NULL || (*value)->child != NULL) goto err_free;

    /* fixing sibling linked list connections */
    if ((*value)->previous && (*value)->next)
    {
        (*value)->previous->next = (*value)->next;
        (*value)->next->previous = (*value)->previous;
    }
    else
    {
        if ((*value)->previous)
        {
            (*value)->previous->next = NULL;
        }
        if ((*value)->next)
        {
            (*value)->next->previous = NULL;
        }
    }

    /*fixing parent node connections */
    if ((*value)->parent)
    {
        /* fix the tree connection to the first node in the children's list */
        if ((*value)->parent->child == (*value))
        {
            if ((*value)->next)
            {
                (*value)->parent->child = (*value)->next;   /* the parent node always points to the first node in the children linked list */
            }
            else
            {
                (*value)->parent->child = NULL;
            }
        }

        /* fix the tree connection to the last node in the children's list */
        if ((*value)->parent->child_end == (*value))
        {
            if ((*value)->previous)
            {
                (*value)->parent->child_end = (*value)->previous;   /* the parent node always points to the last node in the children linked list */
            }
            else
            {
                (*value)->parent->child_end = NULL;
            }
        }
    }

    /*finally, freeing the memory allocated for this value */
    if ((*value)->text != NULL)
    {
        free ((*value)->text);
    }
    free (*value);      /* the json value */
    (*value) = NULL;

    err_free:
        /*printf("error_free \n");*/
        (void)0;
}


void
json_free_value (json_t ** value)
{
    json_t *cursor = *value;

    /*assert (value);
    assert (*value);*/
    if(value == NULL || (*value) == NULL) goto err;

    while (*value)
    {
        json_t *parent;

        if (cursor->child)
        {
            cursor = cursor->child;
            continue;
        }

        if (cursor == *value)
        {
            *value = NULL;
        }

        parent = cursor->parent;
        intern_json_free_value (&cursor);
        cursor = parent;
    }
    err:
        /*printf("json.c 470 can't free\n");*/
        (void)0;
}


enum json_error
json_insert_child (json_t * parent, json_t * child)
{
    /*TODO change the child list from FIFO to LIFO, in order to get rid of the child_end pointer 
    assert (parent != NULL);     the parent must exist 
    assert (child != NULL);  the child must exist 
    assert (parent != child);    parent and child must not be the same. if they are, it will enter an infinite loop */
    if(parent == NULL || child == NULL || parent == child) return JSON_NULL;
    /* enforce tree structure correctness */
    switch (parent->type)
    {
    case JSON_STRING:
        /* a string accepts every JSON type as a child value */
        /* therefore, the sanity check must be performed on the child node */
        switch (child->type)
        {
        case JSON_STRING:
        case JSON_NUMBER:
        case JSON_TRUE:
        case JSON_FALSE:
        case JSON_NULL:
            if (child->child != NULL)
                return JSON_BAD_TREE_STRUCTURE;
            break;

        case JSON_OBJECT:
        case JSON_ARRAY:
            break;

        default:
            return JSON_BAD_TREE_STRUCTURE; /* this part should never be reached */
            break;
        }
        break;

    case JSON_OBJECT:   /* JSON objects may only accept JSON string objects which already have child nodes of their own */
        if (child->type != JSON_STRING)
            return JSON_BAD_TREE_STRUCTURE;
        break;

    case JSON_ARRAY:
        switch (child->type)
        {
        case JSON_STRING:
        case JSON_TRUE:
        case JSON_FALSE:
        case JSON_NULL:
        case JSON_NUMBER:
            if (child->child)
                return JSON_BAD_TREE_STRUCTURE;
            break;

        case JSON_OBJECT:
        case JSON_ARRAY:
            break;

        default:
            return JSON_BAD_TREE_STRUCTURE;
        }
        break;

    default:
        return JSON_BAD_TREE_STRUCTURE;
    }

    child->parent = parent;
    if (parent->child)
    {
        child->previous = parent->child_end;
        parent->child_end->next = child;
        parent->child_end = child;
    }
    else
    {
        parent->child = child;
        parent->child_end = child;
    }

    return JSON_OK;
}


enum json_error
json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value)
{
    enum json_error error;
    json_t *label;

    /* verify if the parameters are valid 
    assert (parent != NULL);
    assert (text_label != NULL);
    assert (value != NULL);
    assert (parent != value);

     enforce type coherence 
    assert (parent->type == JSON_OBJECT);*/

    if(parent == NULL || text_label == NULL || value == NULL || parent == value || parent->type != JSON_OBJECT) return JSON_MEMORY;
 
    /* create label json_value */
    label = json_new_string (text_label);
    if (label == NULL)
        return JSON_MEMORY;

    /*insert value and check for error */
    error = json_insert_child (label, value);
    if (error != JSON_OK)
        return error;
    /*insert value and check for error */
    error = json_insert_child (parent, label);
    if (error != JSON_OK)
        return error;

    return JSON_OK;
}


enum json_error
json_tree_to_string (json_t * root, char **text)
{
    json_t *cursor;
    rcstring *output;

    /*assert (root != NULL);
    assert (text != NULL);*/
    if(root == NULL || text == NULL) return JSON_MEMORY;

    cursor = root;
    /* set up the output and temporary rwstrings */
    output = rcs_create (RSTRING_DEFAULT);

    /* start the convoluted fun */
      state1:           /* open value */
    {
        if ((cursor->previous) && (cursor != root)) /*if cursor is children and not root than it is a followup sibling */
        {
            /* append comma */
            if (rcs_catc (output, ',') != RS_OK)
            {
                return JSON_MEMORY;
            }
        }
        switch (cursor->type)
        {
        case JSON_STRING:
            /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */
            /* set the new output size */
            if (rcs_catc (output, '\"') != RS_OK)
            {
                return JSON_MEMORY;
            }
            if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc (output, '\"') != RS_OK)
            {
                return JSON_MEMORY;
            }

            if (cursor->parent != NULL)
            {
                if (cursor->parent->type == JSON_OBJECT)    /* cursor is label in label:value pair */
                {
                    /* error checking: if parent is object and cursor is string then cursor must have a single child */
                    if (cursor->child != NULL)
                    {
                        if (rcs_catc (output, ':') != RS_OK)
                        {
                            return JSON_MEMORY;
                        }
                    }
                    else
                    {
                        /* malformed document tree: label without value in label:value pair */
                        rcs_free (&output);
                        text = NULL;
                        return JSON_BAD_TREE_STRUCTURE;
                    }
                }
            }
            else/* does not have a parent */
            {
                if (cursor->child != NULL)  /* is root label in label:value pair */
                {
                    if (rcs_catc (output, ':') != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    /* malformed document tree: label without value in label:value pair */
                    rcs_free (&output);
                    text = NULL;
                    return JSON_BAD_TREE_STRUCTURE;
                }
            }
            break;

        case JSON_NUMBER:
            /* must not have any children */
            /* set the new size */
            if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
            {
                return JSON_MEMORY;
            }
            goto state2;    /* close value */
            break;

        case JSON_OBJECT:
            if (rcs_catc (output, '{') != RS_OK)
            {
                return JSON_MEMORY;
            }

            if (cursor->child)
            {
                cursor = cursor->child;
                goto state1;    /* open value */
            }
            else
            {
                goto state2;    /* close value */
            }
            break;

        case JSON_ARRAY:
            if (rcs_catc (output, '[') != RS_OK)
            {
                return JSON_MEMORY;
            }

            if (cursor->child != NULL)
            {
                cursor = cursor->child;
                goto state1;
            }
            else
            {
                goto state2;    /* close value */
            }
            break;

        case JSON_TRUE:
            /* must not have any children */
            if (rcs_catcs (output, "true", 4) != RS_OK)
            {
                return JSON_MEMORY;
            }
            goto state2;    /* close value */
            break;

        case JSON_FALSE:
            /* must not have any children */
            if (rcs_catcs (output, "false", 5) != RS_OK)
            {
                return JSON_MEMORY;
            }
            goto state2;    /* close value */
            break;

        case JSON_NULL:
            /* must not have any children */
            if (rcs_catcs (output, "null", 4) != RS_OK)
            {
                return JSON_MEMORY;
            }
            goto state2;    /* close value */
            break;

        default:
            goto error;
        }
        if (cursor->child)
        {
            cursor = cursor->child;
            goto state1;    /* open value */
        }
        else
        {
            /* does not have any children */
            goto state2;    /* close value */
        }
    }

      state2:           /* close value */
    {
        switch (cursor->type)
        {
        case JSON_OBJECT:
            if (rcs_catc (output, '}') != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case JSON_ARRAY:
            if (rcs_catc (output, ']') != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case JSON_STRING:
            break;
        case JSON_NUMBER:
            break;
        case JSON_TRUE:
            break;
        case JSON_FALSE:
            break;
        case JSON_NULL:
            break;
        default:
            goto error;
        }
        if ((cursor->parent == NULL) || (cursor == root))
        {
            goto end;
        }
        else if (cursor->next)
        {
            cursor = cursor->next;
            goto state1;    /* open value */
        }
        else
        {
            cursor = cursor->parent;
            goto state2;    /* close value */
        }
    }

      error:
    {
        rcs_free (&output);
        return JSON_UNKNOWN_PROBLEM;
    }

      end:
    {
        *text = rcs_unwrap (output);
        return JSON_OK;
    }
}


enum json_error
json_stream_output (FILE * file, json_t * root)
{
    json_t *cursor;

    /*assert (root != NULL);
    assert (file != NULL);   the file stream must be opened */
    if(root == NULL || file == NULL) return JSON_MEMORY;

    cursor = root;
    /* set up the output and temporary rwstrings */

    /* start the convoluted fun */
      state1:           /* open value */
    {
        if ((cursor->previous) && (cursor != root)) /*if cursor is children and not root than it is a followup sibling */
        {
            /* append comma */
            fprintf (file, ",");
        }
        switch (cursor->type)
        {
        case JSON_STRING:
            /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */
            /* set the new output size */
            fprintf (file, "\"%s\"", cursor->text);

            if (cursor->parent != NULL)
            {
                if (cursor->parent->type == JSON_OBJECT)    /* cursor is label in label:value pair */
                {
                    /* error checking: if parent is object and cursor is string then cursor must have a single child */
                    if (cursor->child != NULL)
                    {
                        if (fprintf (file, ":") != RS_OK)
                        {
                            return JSON_MEMORY;
                        }
                    }
                    else
                    {
                        /* malformed document tree: label without value in label:value pair */
                        return JSON_BAD_TREE_STRUCTURE;
                    }
                }
            }
            else/* does not have a parent */
            {
                if (cursor->child != NULL)  /* is root label in label:value pair */
                {
                    fprintf (file, ":");
                }
                else
                {
                    /* malformed document tree: label without value in label:value pair */
                    return JSON_BAD_TREE_STRUCTURE;
                }
            }
            break;

        case JSON_NUMBER:
            /* must not have any children */
            /* set the new size */
            fprintf (file, "%s", cursor->text);
            goto state2;    /* close value */
            break;

        case JSON_OBJECT:
            fprintf (file, "{");

            if (cursor->child)
            {
                cursor = cursor->child;
                goto state1;    /* open value */
            }
            else
            {
                goto state2;    /* close value */
            }
            break;

        case JSON_ARRAY:
            fprintf (file, "[");

            if (cursor->child != NULL)
            {
                cursor = cursor->child;
                goto state1;
            }
            else
            {
                goto state2;    /* close value */
            }
            break;

        case JSON_TRUE:
            /* must not have any children */
            fprintf (file, "true");
            goto state2;    /* close value */
            break;

        case JSON_FALSE:
            /* must not have any children */
            fprintf (file, "false");
            goto state2;    /* close value */
            break;

        case JSON_NULL:
            /* must not have any children */
            fprintf (file, "null");
            goto state2;    /* close value */
            break;

        default:
            goto error;
        }
        if (cursor->child)
        {
            cursor = cursor->child;
            goto state1;    /* open value */
        }
        else
        {
            /* does not have any children */
            goto state2;    /* close value */
        }
    }

      state2:           /* close value */
    {
        switch (cursor->type)
        {
        case JSON_OBJECT:
            fprintf (file, "}");
            break;

        case JSON_ARRAY:
            fprintf (file, "]");
            break;

        case JSON_STRING:
            break;
        case JSON_NUMBER:
            break;
        case JSON_TRUE:
            break;
        case JSON_FALSE:
            break;
        case JSON_NULL:
            break;
        default:
            goto error;
        }
        if ((cursor->parent == NULL) || (cursor == root))
        {
            goto end;
        }
        else if (cursor->next)
        {
            cursor = cursor->next;
            goto state1;    /* open value */
        }
        else
        {
            cursor = cursor->parent;
            goto state2;    /* close value */
        }
    }

      error:
    {
        return JSON_UNKNOWN_PROBLEM;
    }

      end:
    {
        fprintf (file, "\n");
        return JSON_OK;
    }
}


void
json_strip_white_spaces (char *text)
{
    size_t in, out, length;
    int state;

    /*assert (text != NULL);*/
    if(text == NULL) goto err_white;
    in = 0;
    out = 0;
    length = strlen (text);
    state = 0;      /* possible states: 0 -> document, 1 -> inside a string */

    while (in < length)
    {
        switch (text[in])
        {
        case '\x20':    /* space */
        case '\x09':    /* horizontal tab */
        case '\x0A':    /* line feed or new line */
        case '\x0D':    /* Carriage return */
            if (state == 1)
            {
                text[out++] = text[in];
            }
            break;

        case '\"':
            switch (state)
            {
            case 0: /* not inside a JSON string */
                state = 1;
                break;

            case 1: /* inside a JSON string */
                if (text[in - 1] != '\\')
                {
                    state = 0;
                }
                break;

            default:
                assert (0);
            }
            text[out++] = text[in];
            break;

        default:
            text[out++] = text[in];
        }
        ++in;
    }
    text[out] = '\0';

    err_white:
        printf("err_white\n");
}


char *
json_format_string (const char *text)
{
    size_t pos = 0, text_length;
    unsigned int indentation = 0;   /* the current indentation level */
    unsigned int i;     /* loop iterator variable */
    char loop;

    rcstring *output;
    text_length = strlen (text);

    output = rcs_create (text_length);
    while (pos < text_length)
    {
        switch (text[pos])
        {
        /*case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':     JSON insignificant white spaces 
            pos++;
            break;
        */
        case '{':
            indentation++;
            rcs_catcs (output, "{\n", 2);
            for (i = 0; i < indentation; i++)
            {
                rcs_catc (output, '\t');
            }
            pos++;
            break;

        case '}':
            indentation--;
            rcs_catc (output, '\n');
            for (i = 0; i < indentation; i++)
            {
                rcs_catc (output, '\t');
            }
            rcs_catc (output, '}');
            pos++;
            break;

        case ':':
            rcs_catcs (output, ": ", 2);
            pos++;
            break;

        case ',':
            rcs_catcs (output, ",\n", 2);
            for (i = 0; i < indentation; i++)
            {
                rcs_catc (output, '\t');
            }
            pos++;
            break;

        case '\"':  /* open string */
            rcs_catc (output, text[pos]);
            pos++;
            loop = 1;   /* inner string loop trigger is enabled */
            while (loop)
            {
                if (text[pos] == '\\')  /* escaped sequence */
                {
                    rcs_catc (output, '\\');
                    pos++;
                    if (text[pos] == '\"')  /* don't consider a \" escaped sequence as an end of string */
                    {
                        rcs_catc (output, '\"');
                        pos++;
                    }
                }
                else if (text[pos] == '\"') /* reached end of string */
                {
                    loop = 0;
                }

                rcs_catc (output, text[pos]);

                pos++;
                if (pos >= text_length)
                {
                    loop = 0;
                }
            }
            break;

        default:
            rcs_catc (output, text[pos]);
            pos++;
            break;
        }
    }
    return rcs_unwrap (output);
}


char *
json_escape (char *text)
{
    rcstring *output;
    size_t i, length;
    char buffer[7];
    /* check if pre-conditions are met 
    assert (text != NULL);*/
    if(text == NULL) return NULL;

    /* defining the temporary variables */
    length = strlen (text);
    output = rcs_create (length);
    if (output == NULL)
        return NULL;
    for (i = 0; i < length; i++)
    {
        if (text[i] == '\\')
        {
            rcs_catcs (output, "\\\\", 2);
        }
        else if (text[i] == '\"')
        {
            rcs_catcs (output, "\\\"", 2);
        }
        else if (text[i] == '/')
        {
            rcs_catcs (output, "\\/", 2);
        }
        else if (text[i] == '\b')
        {
            rcs_catcs (output, "\\b", 2);
        }
        else if (text[i] == '\f')
        {
            rcs_catcs (output, "\\f", 2);
        }
        else if (text[i] == '\n')
        {
            rcs_catcs (output, "\\n", 2);
        }
        else if (text[i] == '\r')
        {
            rcs_catcs (output, "\\r", 2);
        }
        else if (text[i] == '\t')
        {
            rcs_catcs (output, "\\t", 2);
        }
        else if (text[i] < 0)   /* non-BMP character */
        {
            rcs_catc (output, text[i]);
        }
        else if (text[i] < 0x20)
        {
            snprintf (buffer, 7, "\\u%4.4x", text[i]);
            rcs_catcs (output, buffer, 6);
        }
        else
        {
            rcs_catc (output, text[i]);
        }
    }
    return rcs_unwrap (output);
}


char *
json_unescape (char *text)
{
    if(text == NULL) return NULL;

    char *result;
    size_t r;       /* read cursor */
    size_t w;       /* write cursor */

    result = malloc (strlen (text) + 1);

    for (r = w = 0; text[r]; r++)
    {
        switch (text[r])
        {
        case '\\':
            switch (text[++r])
            {
            case '\"':
            case '\\':
            case '/':
                /* literal translation */
                result[w++] = text[r];
                break;
            case 'b':
                result[w++] = '\b';
                break;
            case 'f':
                result[w++] = '\f';
                break;
            case 'n':
                result[w++] = '\n';
                break;
            case 'r':
                result[w++] = '\r';
                break;
            case 't':
                result[w++] = '\t';
                break;
            case 'u':
                {
                    char buf[5];
                    int64_t unicode;

                    buf[0] = text[++r];
                    buf[1] = text[++r];
                    buf[2] = text[++r];
                    buf[3] = text[++r];
                    buf[4] = '\0';

                    unicode = strtol (buf, NULL, 16);

                    if (unicode < 0x80)
                    {
                        /* ASCII: map to UTF-8 literally */
                        result[w++] = (char) unicode;
                    }
                    else if (unicode < 0x800)
                    {
                        /* two-byte-encoding */
                        char one = (char)0xC0;    /* 110 00000 */
                        char two = (char)0x80;    /* 10 000000 */

                        two += (unicode & 0x3F);
                        unicode >>= 6;
                        one += (unicode & 0x1F);

                        result[w++] = one;
                        result[w++] = two;
                    }
                    else if (unicode < 0x10000)
                    {
                        if (unicode < 0xD800 || 0xDBFF < unicode)
                        {
                            /* three-byte-encoding */
                            char one = (char)0xE0;    /* 1110 0000 */
                            char two = (char)0x80;    /* 10 000000 */
                            char three = (char)0x80;  /* 10 000000 */

                            three += (unicode & 0x3F);
                            unicode >>= 6;
                            two += (unicode & 0x3F);
                            unicode >>= 6;
                            one += (unicode & 0xF);

                            result[w++] = one;
                            result[w++] = two;
                            result[w++] = three;
                        }
                        else
                        {
                            /* unicode is a UTF-16 high surrogate, continue with the low surrogate */
                            uint64_t high_surrogate = unicode;  /* 110110 00;00000000 */
                            uint64_t low_surrogate;
                            char one = (char)0xF0;    /* 11110 000 */
                            char two = (char)0x80;    /* 10 000000 */
                            char three = (char)0x80;  /* 10 000000 */
                            char four = (char)0x80;   /* 10 000000 */

                            if (!text[++r] == '\\')
                            {
                                break;
                            }
                            if (!text[++r] == 'u')
                            {
                                break;
                            }

                            buf[0] = text[++r];
                            buf[1] = text[++r];
                            buf[2] = text[++r];
                            buf[3] = text[++r];

                            low_surrogate = strtol (buf, NULL, 16); /* 110111 00;00000000 */

                            /* strip surrogate markers */
                            high_surrogate -= 0xD800;   /* 11011000;00000000 */
                            low_surrogate -= 0xDC00;    /* 11011100;00000000 */

                            unicode = (high_surrogate << 10) + (low_surrogate) + 0x10000;

                            /* now encode into four-byte UTF-8 (as we are larger than 0x10000) */
                            four += (unicode & 0x3F);
                            unicode >>= 6;
                            three += (unicode & 0x3F);
                            unicode >>= 6;
                            two += (unicode & 0x3F);
                            unicode >>= 6;
                            one += (unicode & 0x7);

                            result[w++] = one;
                            result[w++] = two;
                            result[w++] = three;
                            result[w++] = four;
                        }
                    }
                    else
                    {
                        fprintf (stderr, "JSON: unsupported unicode value: 0x%lX\n", unicode);
                    }
                }
                break;
            default:
                assert (0);
                break;
            }
            break;
        default:
            result[w++] = text[r];
            break;
        }
    }
    result[w] = '\0';

    return result;
}


void
json_jpi_init (struct json_parsing_info *jpi)
{
    /*assert (jpi != NULL);*/
    if(jpi != NULL){
        jpi->state = 0;
        jpi->lex_state = 0;
        jpi->lex_text = NULL;
        jpi->p = NULL;
        jpi->cursor = NULL;
        jpi->line = 1;
        jpi->string_length_limit_reached = 0;
    }
}


int
lexer (char *buffer, char **p, unsigned int *state, rcstring ** text, size_t *line)
{
    /*assert (buffer != NULL);
    assert (p != NULL);
    assert (state != NULL);
    assert (text != NULL);
    assert (line != NULL);*/
    if(buffer == NULL || p == NULL || state == NULL || text == NULL || line == NULL) return LEX_INVALID_CHARACTER;

    if (*p == NULL)
        *p = buffer;

    while (**p != '\0')
    {
        switch (*state)
        {

        case 0: /* Root document */
            {
                switch (*(*p)++)
                {
                case '\x20':    /* space */
                case '\x09':    /* horizontal tab */
                    break;

                case '\x0A':    /* line feed or new line */
                case '\x0D':    /* Carriage return */
                    *line += 1;  /* increment line number */
                    break;

                case '{':
                    return LEX_BEGIN_OBJECT;
                case '}':
                    return LEX_END_OBJECT;
                case '[':
                    return LEX_BEGIN_ARRAY;
                case ']':
                    return LEX_END_ARRAY;
                case ':':
                    return LEX_NAME_SEPARATOR;
                case ',':
                    return LEX_VALUE_SEPARATOR;

                case '\"':
                    *text = rcs_create (RSTRING_DEFAULT);
                    if (*text == NULL)
                        return LEX_MEMORY;
                    *state = 1; /* inside a JSON string */
                    break;

                case 't':
                    *state = 7; /* true: 1 */
                    break;

                case 'f':
                    *state = 10;    /* false: 1 */
                    break;

                case 'n':
                    *state = 14;    /* false: 1 */
                    break;

                case '-':
                    *text = rcs_create (RSTRING_DEFAULT);
                    if (*text == NULL)
                        return LEX_MEMORY;
                    if (rcs_catc (*text, '-') != RS_OK)
                        return LEX_MEMORY;
                    *state = 17;    /* number: '0' */
                    break;

                case '0':
                    *text = rcs_create (RSTRING_DEFAULT);
                    if (*text == NULL)
                        return LEX_MEMORY;
                    if (rcs_catc (*text, '0') != RS_OK)
                        return LEX_MEMORY;
                    *state = 18;    /* number: '0' */
                    break;

                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    *text = rcs_create (RSTRING_DEFAULT);
                    if (*text == NULL)
                        return LEX_MEMORY;
                    if (rcs_catc (*text, *(*p - 1)) != RS_OK)
                        return LEX_MEMORY;
                    *state = 19;    /* number: decimal followup */
                    break;


                default:
                    return LEX_INVALID_CHARACTER;
                }
            }
            break;

        case 1: /* inside a JSON string */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                case 10:    /* line feed */
                case 11:
                case 12:
                case 13:    /* carriage return */
                case 14:
                case 15:
                case 16:
                case 17:
                case 18:
                case 19:
                case 20:
                case 21:
                case 22:
                case 23:
                case 24:
                case 25:
                case 26:
                case 27:
                case 28:
                case 29:
                case 30:
                case 31:
                    /* ASCII control characters can only be present in a JSON string if they are escaped. If not then the document is invalid */
                    return LEX_INVALID_CHARACTER;
                    break;

                case '\"':  /* close JSON string */
                    /* it is expected that, in the routine that calls this function, text is set to NULL */
                    *state = 0;
                    ++*p;
                    return LEX_STRING;
                    break;

                case '\\':
                    if (rcs_catc (*text, '\\') != RS_OK)
                        return LEX_MEMORY;
                    *state = 2; /* inside a JSON string: start escape sequence */
                    break;

                default:
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                }
                ++*p;
            }
            break;

        case 2: /* inside a JSON string: start escape sequence */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '\\':
                case '\"':
                case '/':
                case 'b':
                case 'f':
                case 'n':
                case 'r':
                case 't':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 1; /* inside a JSON string */
                    break;

                case 'u':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 3; /* inside a JSON string: escape unicode */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                }
                ++*p;
            }
            break;

        case 3: /*inside a JSON string: escape unicode */
            {
                assert (*text != NULL);
                if ((**p >= 'a') && (**p <= 'f'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 4; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= 'A') && (**p <= 'F'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 4; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= '0') && (**p <= '9'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 4; /* inside a JSON string: escape unicode */
                }
                else
                    return LEX_INVALID_CHARACTER;
                ++*p;
            }
            break;

        case 4: /* inside a JSON string: escape unicode */
            {
                assert (*text != NULL);
                if ((**p >= 'a') && (**p <= 'f'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 5; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= 'A') && (**p <= 'F'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 5; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= '0') && (**p <= '9'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 5; /* inside a JSON string: escape unicode */
                }
                else
                    return LEX_INVALID_CHARACTER;
                ++*p;
            }
            break;

        case 5: /* inside a JSON string: escape unicode */
            {
                assert (*text != NULL);
                if ((**p >= 'a') && (**p <= 'f'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 6; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= 'A') && (**p <= 'F'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 6; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= '0') && (**p <= '9'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 6; /* inside a JSON string: escape unicode */
                }
                else
                    return LEX_INVALID_CHARACTER;
                ++*p;
            }
            break;

        case 6: /* inside a JSON string: escape unicode */
            {
                assert (*text != NULL);
                if ((**p >= 'a') && (**p <= 'f'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 1; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= 'A') && (**p <= 'F'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 1; /* inside a JSON string: escape unicode */
                }
                else if ((**p >= '0') && (**p <= '9'))
                {
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    *state = 1; /* inside a JSON string: escape unicode */
                }
                else
                    return LEX_INVALID_CHARACTER;
                ++*p;
            }
            break;

        case 7: /* true: 1 */
            {
                switch (*(*p)++)
                {
                case 'r':
                    *state = 8;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 8: /* true: 2 */
            {
                switch (*(*p)++)
                {
                case 'u':
                    *state = 9;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 9: /* true: 3 */
            {
                switch (*(*p)++)
                {
                case 'e':
                    *state = 0;
                    return LEX_TRUE;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 10:    /* false: 1 */
            {
                switch (*(*p)++)
                {
                case 'a':
                    *state = 11;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 11:    /* false: 2 */
            {
                switch (*(*p)++)
                {
                case 'l':
                    *state = 12;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 12:    /* false: 3 */
            {
                switch (*(*p)++)
                {
                case 's':
                    *state = 13;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 13:    /* false: 4 */
            {
                switch (*(*p)++)
                {
                case 'e':
                    *state = 0;
                    return LEX_FALSE;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 14:    /* null: 1 */
            {
                switch (*(*p)++)
                {
                case 'u':
                    *state = 15;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 15:    /* null: 2 */
            {
                switch (*(*p)++)
                {
                case 'l':
                    *state = 16;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 16:    /* null: 3 */
            {
                switch (*(*p)++)
                {
                case 'l':
                    *state = 0;
                    return LEX_NULL;
                    break;
                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 17:    /* number: minus sign */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '0':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 18;    /* number: '0' */
                    break;

                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 19;    /* number: decimal followup */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 18:    /* number: '0' */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '\x20':    /* space */
                case '\x09':    /* horizontal tab */
                case '\x0A':    /* line feed or new line */
                case '\x0D':    /* Carriage return */
                    ++*p;
                case ']':
                case '}':
                case ',':
                    *state = 0;
                    return LEX_NUMBER;
                    break;

                case '.':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 20;    /* number: frac start */
                    break;

                case 'e':
                case 'E':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 22;    /* number: exp start */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 19:    /* number: int followup */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '\x20':    /* space */
                case '\x09':    /* horizontal tab */
                case '\x0A':    /* line feed or new line */
                case '\x0D':    /* Carriage return */
                    ++*p;
                case ']':
                case '}':
                case ',':
                    *state = 0;
                    return LEX_NUMBER;
                    break;

                case '.':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 20;    /* number: frac start */
                    break;

                case 'e':
                case 'E':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 22;    /* number: exp start */
                    break;

                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 20:    /* number: frac start */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 21;    /* number: frac continue */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 21:    /* number: frac continue */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '\x20':    /* space */
                case '\x09':    /* horizontal tab */
                case '\x0A':    /* line feed or new line */
                case '\x0D':    /* Carriage return */
                    ++*p;
                case ']':
                case '}':
                case ',':
                    *state = 0;
                    return LEX_NUMBER;
                    break;

                case 'e':
                case 'E':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 22;    /* number: exp start */
                    break;

                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 22:    /* number: exp start */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '-':
                case '+':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 23;    /* number: exp continue */
                    break;

                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 24;    /* number: exp end */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 23:    /* number: exp continue */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    *state = 24;    /* number: exp end */
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        case 24:    /* number: exp end */
            {
                assert (*text != NULL);
                switch (**p)
                {
                case '\x20':    /* space */
                case '\x09':    /* horizontal tab */
                case '\x0A':    /* line feed or new line */
                case '\x0D':    /* Carriage return */
                    ++*p;
                case ']':
                case '}':
                case ',':
                    *state = 0;
                    return LEX_NUMBER;
                    break;

                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    if (rcs_catc (*text, **p) != RS_OK)
                        return LEX_MEMORY;
                    ++*p;
                    break;

                default:
                    return LEX_INVALID_CHARACTER;
                    break;
                }
            }
            break;

        default:
            fprintf (stderr, "JSON: *state missing: %d\n", *state);
            return LEX_INVALID_CHARACTER;
        }

    }

    *p = NULL;
    return LEX_MORE;
}


enum json_error
json_parse_fragment (struct json_parsing_info *info, char *buffer)
{
    json_t *temp = NULL;

    /*assert (info != NULL);
    assert (buffer != NULL);*/
    if(info == NULL || buffer == NULL) return JSON_MEMORY;
    info->p = buffer;
    while (*info->p != '\0')
    {
        switch (info->state)
        {
        case 0: /* starting point */
            {
                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_BEGIN_OBJECT:
                    info->state = 1;    /* begin object */
                    break;

                case LEX_BEGIN_ARRAY:
                    info->state = 7;
                    break;

                default:
                    /*fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);*/
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 1: /* open object */
            {
                if (info->cursor == NULL)
                {
                    if ((info->cursor = json_new_object ()) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    /* perform tree sanity check */
                    assert ((info->cursor->type == JSON_STRING) || (info->cursor->type == JSON_ARRAY));

                    if ((temp = json_new_object ()) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->cursor = temp;
                    temp = NULL;
                }
                info->state = 2;    /* just entered an object */
            }
            break;

        case 2: /* opened object */
            {
                /*perform tree sanity checks */
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_OBJECT);

                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_STRING:
                    if ((temp = json_new_value (JSON_STRING)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO return value according to the value returned from json_insert_child() */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->cursor = temp;
                    temp = NULL;
                    info->state = 5;    /* label, pre label:value separator */
                    break;

                case LEX_END_OBJECT:
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accept whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                        switch (info->cursor->type)
                        {
                        case JSON_STRING:
                            /* perform tree sanity checks */
                            assert (info->cursor->parent != NULL);

                            info->cursor = info->cursor->parent;
                            if (info->cursor->type != JSON_OBJECT)
                            {
                                return JSON_BAD_TREE_STRUCTURE;
                            }
                            else
                            {
                                info->state = 3;    /* finished adding a field to an object */
                            }
                            break;

                        case JSON_ARRAY:
                            info->state = 9;
                            break;

                        default:
                            return JSON_BAD_TREE_STRUCTURE;
                        }
                    }
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                default:
                        /* this should never run */
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 3: /* finished adding a field to an object */
            {
                /*perform tree sanity checks */
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_OBJECT);

                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_VALUE_SEPARATOR:
                    info->state = 4;    /* sibling, post-object */
                    break;

                case LEX_END_OBJECT:
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* parse until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                        switch (info->cursor->type)
                        {
                        case JSON_STRING:
                            /* perform tree sanity checks */
                            assert (info->cursor->parent != NULL);

                            info->cursor = info->cursor->parent;
                            if (info->cursor->type != JSON_OBJECT)
                            {
                                return JSON_BAD_TREE_STRUCTURE;
                            }
                            else
                            {
                                info->state = 3;    /* finished adding a field to an object */
                            }
                            break;

                        case JSON_ARRAY:
                            info->state = 9;
                            break;

                        default:
                            return JSON_BAD_TREE_STRUCTURE;
                        }
                    }
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                default:
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 4: /* sibling, post-object */
            {
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_OBJECT);

                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_STRING:
                    if ((temp = json_new_value (JSON_STRING)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->cursor = temp;
                    temp = NULL;
                    info->state = 5;
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                case LEX_INVALID_CHARACTER:
                    return JSON_ILLEGAL_CHARACTER;
                    break;

                default:    /* this should never run */
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 5: /* label, pre name separator */
            {
                /* perform tree sanity checks */
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_STRING);

                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_NAME_SEPARATOR:
                    info->state = 6;    /* label, pos label:value separator */
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                default:
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 6: /* label, pos name separator */
            {
                unsigned int value; /* to avoid redundant code */
                /* perform tree sanity checks */
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_STRING);

                switch (value = lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_STRING:
                    if ((temp = json_new_value (JSON_STRING)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO specify the exact error message */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accepts whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                    }
                    temp = NULL;
                    info->state = 3;    /* finished adding a field to an object */
                    break;

                case LEX_NUMBER:
                    if ((temp = json_new_value (JSON_NUMBER)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO specify the exact error message */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accepts whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                    }
                    temp = NULL;
                    info->state = 3;    /* finished adding a field to an object */
                    break;

                case LEX_TRUE:
                    if ((temp = json_new_value (JSON_TRUE)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO specify the exact error message */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accepts whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                    }
                    temp = NULL;
                    info->state = 3;    /* finished adding a field to an object */
                    break;

                case LEX_FALSE:
                    if ((temp = json_new_value (JSON_FALSE)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO specify the exact error message */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accepts whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                    }
                    temp = NULL;
                    info->state = 3;    /* finished adding a field to an object */
                    break;

                case LEX_NULL:
                    if ((temp = json_new_value (JSON_NULL)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        /*TODO specify the exact error message */
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accepts whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                    }
                    temp = NULL;
                    info->state = 3;    /* finished adding a field to an object */
                    break;

                case LEX_BEGIN_OBJECT:
                    info->state = 1;
                    break;

                case LEX_BEGIN_ARRAY:
                    info->state = 7;
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                case LEX_MEMORY:
                    return JSON_MEMORY;
                    break;

                case LEX_INVALID_CHARACTER:
                    return JSON_ILLEGAL_CHARACTER;
                    break;

                default:
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 7: /* open array */
            {
                if (info->cursor == NULL)
                {
                    if ((info->cursor = json_new_array ()) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    /* perform tree sanity checks */
                    assert ((info->cursor->type == JSON_ARRAY) || (info->cursor->type == JSON_STRING));

                    if ((temp = json_new_array ()) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->cursor = temp;
                    temp = NULL;
                }
                info->state = 8;    /* just entered an array */
            }
            break;

        case 8: /* just entered an array */
            {
                /* perform tree sanity checks */
                assert (info->cursor != NULL);
                assert (info->cursor->type == JSON_ARRAY);

                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_STRING:
                    if ((temp = json_new_value (JSON_STRING)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    temp = NULL;
                    info->state = 9;    /* label, pre label:value separator */
                    break;

                case LEX_NUMBER:
                    if ((temp = json_new_value (JSON_NUMBER)) == NULL)
                        return JSON_MEMORY;
                    temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    temp = NULL;
                    info->state = 9;    /* label, pre label:value separator */
                    break;

                case LEX_TRUE:
                    if ((temp = json_new_value (JSON_TRUE)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->state = 9;    /* label, pre label:value separator */
                    break;

                case LEX_FALSE:
                    if ((temp = json_new_value (JSON_FALSE)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->state = 9;    /* label, pre label:value separator */
                    break;

                case LEX_NULL:
                    if ((temp = json_new_value (JSON_NULL)) == NULL)
                        return JSON_MEMORY;
                    if (json_insert_child (info->cursor, temp) != JSON_OK)
                    {
                        return JSON_UNKNOWN_PROBLEM;
                    }
                    info->state = 9;    /* label, pre label:value separator */
                    break;

                case LEX_BEGIN_ARRAY:
                    info->state = 7;    /* open array */
                    break;

                case LEX_END_ARRAY:
                    if (info->cursor->parent == NULL)
                    {
                        /*TODO implement this */
                        info->state = 99;   /* finished document. only accept whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                        switch (info->cursor->type)
                        {
                        case JSON_STRING:
                            if (info->cursor->parent == NULL)
                                return JSON_BAD_TREE_STRUCTURE;
                            else
                            {
                                info->cursor = info->cursor->parent;
                                if (info->cursor->type != JSON_OBJECT)
                                {
                                    return JSON_BAD_TREE_STRUCTURE;
                                }

                                info->state = 3;    /* followup to adding child to array */
                            }
                            break;

                        case JSON_ARRAY:
                            info->state = 9;    /* followup to adding child to array */
                            break;

                        default:
                            return JSON_BAD_TREE_STRUCTURE;
                        }
                    }
                    break;

                case LEX_BEGIN_OBJECT:
                    info->state = 1;    /* open object */
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                case LEX_INVALID_CHARACTER:
                    return JSON_ILLEGAL_CHARACTER;
                    break;

                default:
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 9: /* followup to adding child to array */
            {
                /*TODO perform tree sanity checks */
                assert (info->cursor != NULL);
                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_VALUE_SEPARATOR:
                    info->state = 8;
                    break;

                case LEX_END_ARRAY:
                    if (info->cursor->parent == NULL)
                    {
                        info->state = 99;   /* finished document. only accept whitespaces until EOF */
                    }
                    else
                    {
                        info->cursor = info->cursor->parent;
                        switch (info->cursor->type)
                        {
                        case JSON_STRING:
                            if (info->cursor->parent == NULL)
                            {
                                info->state = 99;   /* finished document. only accept whitespaces until EOF */
                            }
                            else
                            {
                                info->cursor = info->cursor->parent;
                                if (info->cursor->type != JSON_OBJECT)
                                {
                                    return JSON_BAD_TREE_STRUCTURE;
                                }
                                else
                                {
                                    info->state = 3;    /* followup to adding child to array */
                                }
                            }
                            break;

                        case JSON_ARRAY:
                            info->state = 9;    /* followup to adding child to array */
                            break;

                        default:
                            return JSON_BAD_TREE_STRUCTURE;
                        }
                    }
                    break;

                case LEX_MORE:
                    return JSON_INCOMPLETE_DOCUMENT;
                    break;

                default:
                    fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        case 99:    /* finished document. only accept whitespaces until EOF */
            {
                /* perform tree sanity check */
                assert (info->cursor->parent == NULL);
                switch (lexer (buffer, &info->p, &info->lex_state, &info->lex_text, &info->line))
                {
                case LEX_MORE:
                    return JSON_WAITING_FOR_EOF;
                    break;

                case LEX_MEMORY:
                    return JSON_MEMORY;
                    break;

                default:
                    return JSON_MALFORMED_DOCUMENT;
                    break;
                }
            }
            break;

        default:
            fprintf (stderr, "JSON: state %d: defaulted at line %zd\n", info->state, info->line);
            return JSON_UNKNOWN_PROBLEM;
        }
    }
    info->p = NULL;
    if (info->state == 99)
        return JSON_WAITING_FOR_EOF;
    else
        return JSON_INCOMPLETE_DOCUMENT;
}



enum json_error
json_parse_document (json_t ** root, char *text)
{
    enum json_error error;
    struct json_parsing_info *jpi;

    /*assert (root != NULL);
    assert (*root == NULL);
    assert (text != NULL);*/
    if(root == NULL || *root != NULL || text == NULL) return JSON_MEMORY;
    /* initialize the parsing structure */
    jpi = malloc (sizeof (struct json_parsing_info));
    if (jpi == NULL)
    {
        return JSON_MEMORY;
    }
    json_jpi_init (jpi);

    error = json_parse_fragment (jpi, text);
    if ((error == JSON_WAITING_FOR_EOF) || (error == JSON_OK))
    {
        *root = jpi->cursor;
        free (jpi);
        return JSON_OK;
    }
    else
    {
        free (jpi);
        return error;
    }
}


enum json_error
json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c)
{
    /*TODO handle a string instead of a single char */
    /* temp variables */
    rcstring *temp;

    /* make sure everything is in it's place 
    assert (jsps != NULL);
    assert (jsf != NULL);*/

    if(jsps == NULL || jsf == NULL)return JSON_MEMORY;
    temp = NULL;

    /* goto where we left off */
    switch (jsps->state)
    {
    case 0:     /* general state. everything goes. */
        goto state0;
        break;
    case 1:     /* parse string */
        goto state1;
        break;
    case 2:     /* parse string: escaped character */
        goto state2;
        break;
    case 3:     /* parse string: escaped unicode 1 */
        goto state3;
        break;
    case 4:     /* parse string: escaped unicode 2 */
        goto state4;
        break;
    case 5:     /* parse string: escaped unicode 3 */
        goto state5;
        break;
    case 6:     /* parse string: escaped unicode 4 */
        goto state6;
        break;
    case 7:     /* parse true: tr */
        goto state7;
        break;
    case 8:     /* parse true: tru */
        goto state8;
        break;
    case 9:     /* parse true: true */
        goto state9;
        break;
    case 10:        /* parse false: fa */
        goto state10;
        break;
    case 11:        /* parse false: fal */
        goto state11;
        break;
    case 12:        /* parse false: fals */
        goto state12;
        break;
    case 13:        /* parse false: false */
        goto state13;
        break;
    case 14:        /* parse null: nu */
        goto state14;
        break;
    case 15:        /* parse null: nul */
        goto state15;
        break;
    case 16:        /* parse null: null */
        goto state16;
        break;
    case 17:        /* parse number: 0 */
        goto state17;
        break;
    case 18:        /* parse number: start fraccional part */
        goto state18;
        break;
    case 19:        /* parse number: fraccional part */
        goto state19;
        break;
    case 20:        /* parse number: start exponent part */
        goto state20;
        break;
    case 21:        /* parse number: exponent part */
        goto state21;
        break;
    case 22:        /* parse number: exponent sign part */
        goto state22;
        break;
    case 23:        /* parse number: start negative */
        goto state23;
        break;
    case 24:        /* parse number: decimal part */
        goto state24;
        break;
    case 25:        /* open object */
        goto state25;
        break;
    case 26:        /* close object/array */
        goto state26;
        break;
    case 27:        /* sibling followup */
        goto state27;
        break;

    default:        /* oops... this should never be reached */
        return JSON_UNKNOWN_PROBLEM;
    }

      state0:           /* starting point */
    {
        switch (c)
        {
        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            break;

        case '\"':  /* starting a string */
            jsps->string_length_limit_reached = 0;
            jsps->state = 1;
            break;

        case '{':
            if (jsf->open_object != NULL)
                jsf->open_object ();
            jsps->state = 25;   /*open object */
            break;

        case '}':
            if (jsf->close_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object/array */
            break;

        case '[':
            if (jsf->open_array != NULL)
                jsf->open_array ();
/*                      jsps->state = 0;        // redundant*/
            break;

        case ']':
            if (jsf->close_array != NULL)
                jsf->close_array ();
            jsps->state = 26;   /* close object/array */
            break;

        case 't':
            jsps->state = 7;    /* parse true: tr */
            break;

        case 'f':
            jsps->state = 10;   /* parse false: fa */
            break;

        case 'n':
            jsps->state = 14;   /* parse null: nu */
            break;

        case ':':
            if (jsf->label_value_separator != NULL)
                jsf->label_value_separator ();
/*                      jsps->state = 0;        // redundant*/
            break;

        case ',':
            if (jsf->sibling_separator != NULL)
                jsf->sibling_separator ();
            jsps->state = 27;   /* sibling followup */
            break;

        case '0':
            jsps->string_length_limit_reached = 0;
            jsps->state = 17;   /* parse number: 0 */
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '0') != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            jsps->string_length_limit_reached = 0;
            jsps->state = 24;   /* parse number: decimal */
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), c) != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case '-':
            jsps->string_length_limit_reached = 0;
            jsps->state = 23;   /* number: */
            jsps->temp = NULL;
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '-') != RS_OK)
            {
                return JSON_MEMORY;
            }

            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state1:           /* parse string */
    {
        switch (c)
        {
        case '\\':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) /* check if there is space for a two character escape sequence */
                {
                    if (rcs_catc ((jsps->temp), '\\') != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 2;    /* parse string: escaped character */
            break;

        case '\"':  /* end of string */
            if ((jsps->temp) != NULL)
            {
                jsps->state = 0;    /* starting point */
                if (jsf->new_string != NULL)
                    jsf->new_string (((jsps->temp))->text); /*copied or integral? */
                rcs_free (&jsps->temp);
            }
            else
                return JSON_UNKNOWN_PROBLEM;
            break;

        default:
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) /* check if there is space for a two character escape sequence */
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            break;
        }
        return JSON_OK;
    }

      state2:           /* parse string: escaped character */
    {
        switch (c)
        {
        case '\"':
        case '\\':
        case '/':
        case 'b':
        case 'f':
        case 'n':
        case 'r':
        case 't':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            break;

        case 'u':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 4)
                {
                    if (rcs_catc ((jsps->temp), 'u') != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 3;    /* parse string: escaped unicode 1; */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state3:           /* parse string: escaped unicode 1 */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 3)
                {
                    if (rcs_catc ((jsps->temp), 'u') != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 4;    /* parse string. escaped unicode 2 */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
        }
        return JSON_OK;
    }

      state4:           /* parse string: escaped unicode 2 */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 2)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 5;    /* parse string. escaped unicode 3 */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
        }
        return JSON_OK;
    }

      state5:           /* parse string: escaped unicode 3 */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 6;    /* parse string. escaped unicode 4 */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
        }
        return JSON_OK;
    }

      state6:           /* parse string: escaped unicode 4 */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 1;    /* parse string */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
        }
        return JSON_OK;
    }

      state7:           /* parse true: tr */
    {
        if (c != 'r')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 8;    /* parse true: tru */
        return JSON_OK;
    }

      state8:           /* parse true: tru */
    {
        if (c != 'u')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 9;    /* parse true: true */
        return JSON_OK;
    }

      state9:           /* parse true: true */
    {
        if (c != 'e')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 0;    /* back to general state. */
        if (jsf->new_true != NULL)
            jsf->new_true ();
        return JSON_OK;
    }

      state10:          /* parse false: fa */
    {
        if (c != 'a')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 11;   /* parse true: fal */
        return JSON_OK;
    }

      state11:          /* parse false: fal */
    {
        if (c != 'l')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 12;   /* parse true: fals */
        return JSON_OK;
    }

      state12:          /* parse false: fals */
    {
        if (c != 's')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 13;   /* parse true: false */
        return JSON_OK;
    }

      state13:          /* parse false: false */
    {
        if (c != 'e')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 0;    /* general state. everything goes. */
        if (jsf->new_false != NULL)
            jsf->new_false ();
        return JSON_OK;
    }

      state14:          /* parse null: nu */
    {
        if (c != 'u')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 15;   /* parse null: nul */
        return JSON_OK;
    }

      state15:          /* parse null: nul */
    {
        if (c != 'l')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 16;   /* parse null: null */
        return JSON_OK;
    }

      state16:          /* parse null: null */
    {
        if (c != 'l')
        {
            return JSON_ILLEGAL_CHARACTER;
        }

        jsps->state = 0;    /* general state. everything goes. */
        if (jsf->new_null != NULL)
            jsf->new_null ();
        return JSON_OK;
    }

      state17:          /* parse number: 0 */
    {
        switch (c)
        {
        case '.':
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '.') != RS_OK)
            {
                return JSON_MEMORY;
            }
            jsps->state = 18;   /* parse number: fraccional part */
            break;

        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            jsps->state = 0;
            break;

        case '}':
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object/array */
            break;

        case ']':

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_array ();
            jsps->state = 26;   /* close object/array */
            break;

        case ',':

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->label_value_separator ();
            jsps->state = 27;   /* sibling followup */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }

        return JSON_OK;
    }

      state18:          /* parse number: start fraccional part */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 19;   /* parse number: fractional part */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state19:          /* parse number: fraccional part */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
/*                      jsps->state = 19;       // parse number: fractional part*/
            break;

        case 'e':
        case 'E':
            if (rcs_catc ((jsps->temp), c) != RS_OK)
            {
                return JSON_MEMORY;
            }

            jsps->state = 20;   /* parse number: start exponent part */
            break;


        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            jsps->state = 0;
            break;

        case '}':

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object/array */
            break;

        case ']':
            if (jsf->new_number != NULL)
            {
                if ((jsps->temp) == NULL)
                    return JSON_MEMORY;
                jsf->new_number ((jsps->temp)->text);
                rcs_free (&jsps->temp);
            }
            else
            {
                rcs_free (&jsps->temp);
                jsps->temp = NULL;
            }
            if (jsf->open_object != NULL)
                jsf->close_array ();
            jsps->state = 26;   /* close object/array */
            break;

        case ',':

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->label_value_separator != NULL)
                jsf->label_value_separator ();
            jsps->state = 27;   /* sibling followup */
            break;


        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state20:          /* parse number: start exponent part */
    {
        switch (c)
        {
        case '+':
        case '-':
            jsps->string_length_limit_reached = 0;
            if (rcs_catc ((jsps->temp), c) != RS_OK)
            {
                return JSON_MEMORY;
            }

            jsps->state = 22;   /* parse number: exponent sign part */
            break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }

                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 21;   /* parse number: exponent part */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state21:          /* parse number: exponent part */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
                {
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
/*                              jsps->state = 21;       // parse number: exponent part*/
            break;

        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */

            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            jsps->state = 0;
            break;

        case '}':
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object */
            break;

        case ']':
            if (jsf->new_number != NULL)
            {
                if ((jsps->temp) == NULL)
                    return JSON_MEMORY;
                jsf->new_number ((jsps->temp)->text);
                free (jsps->temp);
                jsps->temp = NULL;
            }
            else
            {
                free ((jsps->temp));
                jsps->temp = NULL;
            }
            if (jsf->open_object != NULL)
                jsf->close_array ();
            jsps->state = 26;   /* close object/array */
            break;

        case ',':
            if (jsf->new_number != NULL)
            {
                if ((jsps->temp) == NULL)
                    return JSON_MEMORY;
                jsf->new_number ((jsps->temp)->text);
                free ((jsps->temp));
                jsps->temp = NULL;
            }
            else
            {
                free (jsps->temp);
                jsps->temp = NULL;
            }
            if (jsf->label_value_separator != NULL)
                jsf->label_value_separator ();
            jsps->state = 27;   /* sibling followup */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state22:          /* parse number: start exponent part */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
                {
                    rcs_catc ((jsps->temp), c);
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
            jsps->state = 21;   /* parse number: exponent part */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state23:          /* parse number: start negative */
    {
        switch (c)
        {
        case '0':
            rcs_catc ((jsps->temp), c);
            jsps->state = 17;   /* parse number: 0 */
            break;

        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
                {
                    if ((jsps->temp = rcs_create (5)) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                    else
                    {
                        jsps->string_length_limit_reached = 1;
                    }
                }
            }
            jsps->state = 24;   /* parse number: start decimal part */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state24:          /* parse number: decimal part */
    {
        switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            if (!jsps->string_length_limit_reached)
            {
                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
                {
                    if ((jsps->temp = rcs_create (5)) == NULL)
                    {
                        return JSON_MEMORY;
                    }
                    if (rcs_catc ((jsps->temp), c) != RS_OK)
                    {
                        return JSON_MEMORY;
                    }
                }
                else
                {
                    jsps->string_length_limit_reached = 1;
                }
            }
/*                              jsps->state = 24;       // parse number: decimal part*/
            break;

        case '.':
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '.') != RS_OK)
            {
                return JSON_MEMORY;
            }

            jsps->state = 18;   /* parse number: start exponent part */
            break;

        case 'e':
        case 'E':
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), c) != RS_OK)
            {
                return JSON_MEMORY;
            }

            jsps->string_length_limit_reached = 0;  /* reset to accept the exponential part */
            jsps->state = 20;   /* parse number: start exponent part */
            break;

        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            jsps->state = 0;
            break;

        case '}':
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object/array */
            break;

        case ']':
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->open_object != NULL)
                jsf->close_array ();
            jsps->state = 26;   /* close object/array */
            break;

        case ',':
            if ((jsps->temp) == NULL)
                return JSON_MEMORY;
            if (jsf->new_number != NULL)
            {
                jsf->new_number ((jsps->temp)->text);
            }
            rcs_free (&jsps->temp);

            if (jsf->label_value_separator != NULL)
                jsf->label_value_separator ();
            jsps->state = 27;   /* sibling followup */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state25:          /* open object */
    {
        switch (c)
        {
        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            break;

        case '\"':
            jsps->temp = NULL;
            jsps->state = 1;
            break;

        case '}':
            if (jsf->close_object != NULL)
                jsf->close_object ();
            jsps->state = 26;   /* close object */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state26:          /* close object/array */
    {
        switch (c)
        {
        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            break;

        case '}':
            if (jsf->close_object != NULL)
                jsf->close_object ();
/*                      jsp->state = 26;        // close object*/
            break;

        case ']':
            if (jsf->close_array != NULL)
                jsf->close_array ();
/*                      jsps->state = 26;       // close object/array*/
            break;

        case ',':
            if (jsf->sibling_separator != NULL)
                jsf->sibling_separator ();
            jsps->state = 27;   /* sibling followup */
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

      state27:          /* sibling followup */
    {
        switch (c)
        {
        case '\x20':
        case '\x09':
        case '\x0A':
        case '\x0D':    /* JSON insignificant white spaces */
            break;

        case '\"':
            jsps->state = 1;
            jsps->temp = NULL;
            break;

        case '{':
            if (jsf->open_object != NULL)
                jsf->open_object ();
            jsps->state = 25;   /*open object */
            break;

        case '[':
            if (jsf->open_array != NULL)
                jsf->open_array ();
/*                      jsps->state = 0;        // redundant*/
            break;

        case 't':
            jsps->state = 7;    /* parse true: tr */
            break;

        case 'f':
            jsps->state = 10;   /* parse false: fa */
            break;

        case 'n':
            jsps->state = 14;   /* parse null: nu */
            break;

        case '0':
            jsps->state = 17;   /* parse number: 0 */
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '0') != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            jsps->state = 24;   /* parse number: decimal */
            if ((jsps->temp = rcs_create (5)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), c) != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        case '-':
            jsps->state = 23;   /* number: */
            if ((jsps->temp = rcs_create (RSTRING_DEFAULT)) == NULL)
            {
                return JSON_MEMORY;
            }
            if (rcs_catc ((jsps->temp), '-') != RS_OK)
            {
                return JSON_MEMORY;
            }
            break;

        default:
            return JSON_ILLEGAL_CHARACTER;
            break;
        }
        return JSON_OK;
    }

    return JSON_UNKNOWN_PROBLEM;
}

json_t *
json_find_first_label (const json_t * object, const char *text_label)
{
    json_t *cursor;

    /*assert (object != NULL);
    assert (text_label != NULL);
    assert (object->type == JSON_OBJECT);*/
    if(object == NULL || text_label == NULL || object->type != JSON_OBJECT) return NULL;

    for (cursor = object->child; cursor != NULL; cursor = cursor->next)
    {
        if (strcmp (cursor->text, text_label) == 0)
            break;
    }
    return cursor;
}

char *json_get_tree_string(const json_t * object){
    char *new;

    if(object == NULL)return NULL;
    if(object->child == NULL) return NULL;
    if(object->child->text == NULL) return NULL;

    new = strdup(object->child->text);
    return new;
}

char * string_to_json(char *in)
{
  char *new;
  new = json_escape(in);
  return (new);
}
